#pragma once
#include <list>
#include <map>
#include "Iterator.h"
struct IDirect3DVertexShader9;
struct IDirect3DPixelShader9;
class VertexFactoryType;

struct ShaderDefineMap : public std::map<std::string, std::string>
{
	void AddDefine(const std::string& name, const std::string& code)
	{
		(*this)[name] = code;
	}

	// format is "float time;" or "sampler2D diffuseMap;"
	void AddVariable(const std::string& name)
	{
		const std::string varDeclearFlag("VAR_DECLEAR");
		iterator it = find(varDeclearFlag);
		if (it == end())
			AddDefine(varDeclearFlag, name);
		else
			it->second += name;
	}
};

class ShaderType
{
public:
	typedef bool (*ShouldCacheType)(const class Material*);
	typedef Shader* (*CreateType)();
	typedef void (*DefineType)(ShaderDefineMap* defineMap);
	typedef std::list<const ShaderType*> ShaderTypeList;

	enum EShaderType 
	{
		EST_Vertex,
		EST_Pixel,
	};

	static ShaderTypeList& getShaderTypeList() 
	{
		static ShaderTypeList msShaderTypeList;
		return msShaderTypeList; 
	}

public:
	ShaderType(const std::string& source, const std::string& target, const std::string& entry, EShaderType type, 
		ShouldCacheType shouldCache, CreateType create, DefineType define)
		: SourceFile(source), Target(target) , Entry(entry), Type(type)
		, ShouldCache(shouldCache), Create(create), Define(define)
	{
		getShaderTypeList().push_back(this);
	}

	const std::string SourceFile;
	const std::string Target;
	const std::string Entry;
	const EShaderType Type;
	ShouldCacheType ShouldCache;
	CreateType Create;
	DefineType Define;
};

class Shader
{
public:
	enum { INVALIDE_INDEX = -1 };
	typedef std::map<std::string, size_t> NameIndexMap;
	typedef ConstMapIterator<NameIndexMap> ParamsIterator;

public:
	Shader();
	virtual ~Shader();

	IDirect3DVertexShader9* getVertexShader() { return mVertexShader; }
	IDirect3DPixelShader9* getPixelShader() { return mPixelShader; }

	virtual bool Init();
	virtual void Set(RenderView* view) {}
	virtual void SetRenderable(RenderView* view, Renderable* renderable) {}

	size_t GetParamIndex(const std::string& name) const;
	ParamsIterator GetParamIterator() const;

	static Shader* Compile(
		const ShaderType* shaderType, 
		const VertexFactoryType* vertexFactoryType, 
		const ShaderDefineMap* defineMap);

protected:
	NameIndexMap mParamNameIndexMap;
	IDirect3DVertexShader9* mVertexShader;
	IDirect3DPixelShader9* mPixelShader;
	const VertexFactoryType* mVertexFactoryType;
};

inline size_t Shader::GetParamIndex(const std::string& name) const
{
	NameIndexMap::const_iterator it = mParamNameIndexMap.find(name);
	if (it != mParamNameIndexMap.end())
		return it->second;
	return INVALIDE_INDEX;
}

inline Shader::ParamsIterator Shader::GetParamIterator() const
{
	return Shader::ParamsIterator(mParamNameIndexMap);
}